<template>
  <div class="book-speaking">
    <detail-title @back="back" ref="title"></detail-title>
    <scroll
      class="content-wrapper"
      :top="42"
      :bottom="scrollBottom"
      :ifNoScroll="disableScroll"
      @onScroll="onScroll"
      ref="scroll">
      <book-info :cover="cover"
                 :title="title"
                 :author="author"
                 :desc="desc">
      </book-info>
      <div class="book-speak-title-wrapper">
        <div class="icon-speak-wrapper">
          <span class="iconfont">&#xe60e;</span>
        </div>
        <div class="speak-title-wrapper">
          <span class="speak-title">{{$t('speak.voice')}}</span>
        </div>
        <div class="icon-down-wrapper"
             @click="toggleContent">
          <span class="iconfont">{{ifShowContent ? '&#xe60c;' : '&#xe69b;'}}</span>
        </div>
      </div>
      <div class="book-detail-content-wrapper" v-show="ifShowContent">
        <div class="book-detail-content-list-wrapper">
          <div class="loading-text-wrapper" v-if="!this.navigation">
            <span class="loading-text">{{$t('detail.loading')}}</span>
          </div>
          <div class="book-detail-content-item-wrapper">
            <div class="book-detail-content-item"
                 v-for="(item, index) in flatNavigation"
                 :key="index" @click="speak(item, index)">
              <speak-playing v-if="playingIndex === index"
                             :number="5"
                             ref="speakPlaying">
              </speak-playing>
              <div class="book-detail-content-navigation-text"
                   :class="{'is-playing': playingIndex === index}"
                   v-if="item.label">
                {{item.label}}
              </div>
            </div>
          </div>
        </div>
      </div>
      <!--      隐藏的播放器，如果添加controls = controls-->
      <audio @canplay="onCanPlay"
             @timeupdate="onTimeUpdate"
             @ended="onAudioEnded"
             ref="audio">
      </audio>
    </scroll>
    <speak-bottom :chapter="chapter"
                  :currentSectionIndex="currentSectionIndex"
                  :currentSectionTotal="currentSectionTotal"
                  :showPlay="showPlay"
                  :bookItem="bookItem"
                  :isPlaying.sync="isPlaying"
                  :playInfo="playInfo"
                  @onPlayingCardClick="onPlayingCardClick">
    </speak-bottom>
    <div class="book-wrapper">
      <div id="read"></div>
    </div>
    <speak-mask :title="this.chapter ? this.chapter.label : ''"
                :book="book"
                :section="section"
                :currentSectionIndex.sync="currentSectionIndex"
                :currentSectionTotal="currentSectionTotal"
                :isPlaying.sync="isPlaying"
                :playInfo="playInfo"
                @updateText="updateText"
                ref="speakWindow"
    ></speak-mask>
  </div>
</template>

<script>
import DetailTitle from '../../components/detail/DetaiTitle'
import Scroll from '../../components/common/Scroll'
import BookInfo from '../../components/detail/BookInfo'
import SpeakPlaying from '../../components/speak/SpeakPlaying'
import SpeakBottom from '../../components/speak/SpeakBottom'
import SpeakMask from '../../components/speak/SpeakMask'
import Epub from 'epubjs'
import { getCategoryName, findBook } from '../../utils/store'
import { getLocalForage } from '../../utils/localForage'
import { download, flatList } from '../../api/store'
import { realPx } from '../../utils/utils'

global.ePub = Epub

export default {
  name: 'StoreSpeaking',
  components: {
    SpeakMask,
    SpeakBottom,
    SpeakPlaying,
    BookInfo,
    Scroll,
    DetailTitle
  },
  computed: {
    // 取当前分钟
    currentMinute () {
      const m = Math.floor(this.currentPlayingTime / 60)
      // 补零操作
      return m < 10 ? '0' + m : m
    },
    // 取当前秒数
    currentSecond () {
      // 秒数等于当前分钟数乘以60，剩下的时间就是秒数
      const s = Math.floor(this.currentPlayingTime - parseInt(this.currentMinute) * 60)
      return s < 10 ? '0' + s : s
    },
    // 取总分钟
    totalMinute () {
      const m = Math.floor(this.totalPlayingTime / 60)
      return m < 10 ? '0' + m : m
    },
    // 取总秒数
    totalSecond () {
      const s = Math.floor(this.totalPlayingTime - parseInt(this.totalMinute) * 60)
      return s < 10 ? '0' + s : s
    },
    // 取剩余时长
    leftMinute () {
      // 总时长减去当前播放的时长
      const m = Math.floor((this.totalPlayingTime - this.currentPlayingTime) / 60)
      return m < 10 ? '0' + m : m
    },
    leftSecond () {
      const s = Math.floor((this.totalPlayingTime - this.currentPlayingTime) - parseInt(this.leftMinute) * 60)
      return s < 10 ? '0' + s : s
    },
    // 播放信息时间
    playInfo () {
      if (this.audioCanPlay) {
        return {
          currentMinute: this.currentMinute,
          currentSecond: this.currentSecond,
          totalMinute: this.totalMinute,
          totalSecond: this.totalSecond,
          leftMinute: this.leftMinute,
          leftSecond: this.leftSecond
        }
      } else {
        return null
      }
    },
    // 播放语种
    lang () {
      return this.metadata ? this.metadata.language : ''
    },
    // 是否处于滚动状态，看窗体有没有打开
    disableScroll () {
      if (this.$refs.speakWindow) {
        return this.$refs.speakWindow.visible
      } else {
        return false
      }
    },
    // 是否显示bottom组件
    showPlay () {
      return this.playingIndex >= 0
    },
    // 动画刷新滚动条
    scrollBottom () {
      return this.showPlay ? 116 : 52
    },
    // 具体点击章节
    chapter () {
      return this.flatNavigation[this.playingIndex]
    },
    // 书本详情，字符串要截取
    desc () {
      if (this.description) {
        // 截取字符串
        return this.description.substring(0, 100)
      } else {
        return ''
      }
    },
    // 完整目录信息
    flatNavigation () {
      if (this.navigation) {
        return Array.prototype.concat.apply([], Array.prototype.concat.apply([], this.doFlatNavigation(this.navigation.toc)))
      } else {
        return []
      }
    },
    category () {
      return this.bookItem ? getCategoryName(this.bookItem.category) : ''
    },
    title () {
      return this.metadata ? this.metadata.title : ''
    },
    author () {
      return this.metadata ? this.metadata.creator : ''
    }
  },
  data () {
    return {
      bookItem: null,
      book: null,
      rendition: null,
      metadata: null,
      cover: null,
      navigation: null,
      description: null,
      ifShowContent: true,
      playingIndex: -1,
      paragraph: null,
      currentSectionIndex: null,
      currentSectionTotal: null,
      section: null,
      isPlaying: false,
      audio: null,
      audioCanPlay: false,
      currentPlayingTime: 0,
      totalPlayingTime: 0,
      playStatus: 0, // 0 未播放，1 播放中，2 暂停中
      toastText: '',
      isOnline: false
    }
  },
  methods: {
    // 2.1.1 生成语音合成信息
    createVoice (text) {
      // 为了兼容苹果， 所以使用XML
      // 使用xml发送请求，原生Ajax
      const xmlhttp = new XMLHttpRequest()
      // 2.1.2调用服务端的API，传入文本以及语种，false表示使用同步的方式调用
      xmlhttp.open('GET', `${process.env.VUE_APP_VOICE_URL}/voice?text=${text}&lang=${this.lang.toLowerCase()}`, false)
      // 2.1.3 发送请求
      xmlhttp.send()
      // 2.1.4 获取响应的信息
      const xmlDoc = xmlhttp.responseText
      if (xmlDoc) {
        // 2.1.5解析
        const json = JSON.parse(xmlDoc)
        // json.path是下载的地址
        if (json.path) {
          // 2.1.6将音频地址设置为该地址
          this.$refs.audio.src = json.path
          // 继续播放
          this.continuePlay()
        } else {
          this.showToast('播放失败，未生成链接')
        }
      } else {
        this.showToast('播放失败')
      }
      /*
        axios.create({
          baseURL: process.env.VUE_APP_VOICE_URL + '/voice'
        })({
          method: 'get',
          params: {
            text: text,
            lang: this.lang.toLowerCase()
          }
        }).then(response => {
          if (response.status === 200) {
            if (response.data.error === 0) {
              const downloadUrl = response.data.path
              console.log('开始下载...%s', downloadUrl)
              downloadMp3(downloadUrl, blob => {
                const url = window.URL.createObjectURL(blob)
                console.log(blob, url)
                this.$refs.audio.src = url
                this.continuePlay()
              })
            } else {
              this.showToast(response.data.msg)
            }
          } else {
            this.showToast('请求失败')
          }
        }).catch(err => {
          console.log(err)
          this.showToast('播放失败')
        })
        */
    },
    // 2、播放与暂停
    togglePlay () {
      // 是否处于播放状态
      if (!this.isPlaying) {
        if (this.playStatus === 0) {
          // 2.1开始播放
          this.play()
        } else if (this.playStatus === 2) {
          // 2.2继续播放
          this.continuePlay()
        }
      } else {
        // 2.3暂停播放
        this.pausePlay()
      }
    },
    // 开启听书
    // 1、点击目录时，就调用该方法，进行初始化
    speak (item, index) {
      // 1.1重置播放器
      this.resetPlay()
      // 获取播放的具体条目
      this.playingIndex = index
      // 刷新滚动条，由于底部变化了
      this.$nextTick(() => {
        this.$refs.scroll.refresh()
      })
      // 1.2假如文本存在
      if (this.chapter) {
        // 1.2.1根据具体目录章节的链接，获取该章节的section信息
        this.section = this.book.spine.get(this.chapter.href)
        // 1.2.2进行渲染
        this.rendition.display(this.section.href).then(section => {
          // 1.2.3渲染后获取当前页面
          const currentPage = this.rendition.currentLocation()
          // 1.2.4获取相应的信息，如cfiBase,cfiStart等，主要用于做文本定位
          const cfiBase = section.cfiBase
          const cfiStart = currentPage.start.cfi.replace(/.*!/, '').replace(/\)/, '')
          const cfiEnd = currentPage.end.cfi.replace(/.*!/, '').replace(/\)/, '')
          this.currentSectionIndex = currentPage.start.displayed.page
          this.currentSectionTotal = currentPage.start.displayed.total
          // 1.2.4拼接cfi
          const cfi = `epubcfi(${cfiBase}!,${cfiStart},${cfiEnd})`
          // console.log(currentPage, cfi, cfiBase, cfiStart, cfiEnd)
          // 1.2.5根据cfi作为参数，传递给book的getRange方法，获取文本
          this.book.getRange(cfi).then(range => {
            let text = range.toLocaleString()
            text = text.replace(/\s(2,)/g, '')
            text = text.replace(/\r/g, '')
            text = text.replace(/\n/g, '')
            text = text.replace(/\t/g, '')
            text = text.replace(/\f/g, '')
            // 1.2.6将文本切割后，传输到VUEX中
            this.updateText(text)
          })
        })
      }
    },
    // 1.1重置播放器
    resetPlay () {
      // 假如正在播放中，暂停播放
      if (this.playStatus === 1) {
        this.pausePlay()
      }
      // 重置状态，改为未播放状态
      this.isPlaying = false
      this.playStatus = 0
    },
    // 2.1开始播放，将文本传输，调用API
    play () {
      this.createVoice(this.paragraph)
    },
    // 2.1.7 继续播放
    continuePlay () {
      // 2.2 调用插件的播放方法
      this.$refs.audio.play().then(() => {
        // 2.2.1开始播放时候的配置
        this.$refs.speakPlaying[0].startAnimation()
        this.isPlaying = true
        this.playStatus = 1
      })
    },
    // 2.3暂停播放
    pausePlay () {
      // 2.3.1暂停播放器
      this.$refs.audio.pause()
      // 2.3.2停止播放动画，由于该组件使用了v-if，所以只会存在一个
      this.$refs.speakPlaying[0].stopAnimation()
      // 2.3.3暂停状态，状态为2
      this.isPlaying = false
      this.playStatus = 2
    },
    //         ******************************   //
    // 当音频播放结束
    onAudioEnded () {
      // 重置播放
      this.resetPlay()
      this.currentPlayingTime = this.$refs.audio.currentTime
      const percent = Math.floor((this.currentPlayingTime / this.totalPlayingTime) * 100)
      // 更新窗体进度
      this.$refs.speakWindow.refreshProgress(percent)
    },
    // 更新时间,时刻
    onTimeUpdate () {
      this.currentPlayingTime = this.$refs.audio.currentTime
      const percent = Math.floor((this.currentPlayingTime / this.totalPlayingTime) * 100)
      this.$refs.speakWindow.refreshProgress(percent)
    },
    // audio设置src成功后，就调用该方法
    onCanPlay () {
      this.audioCanPlay = true
      // 当前播放的秒数
      this.currentPlayingTime = this.$refs.audio.currentTime
      // 总秒数
      this.totalPlayingTime = this.$refs.audio.duration
    },
    // ********************************************  //
    // 0.2去所有书本里找电子书
    findBookFrommList (fileName) {
      flatList().then(res => {
        if (res.status === 200) {
          // 0.2.1找到该书本
          const bookList = res.data.data.filter(item => item.fileName === fileName)
          if (bookList && bookList.length > 0) {
            // 0.2.2赋值书本信息，然后重新初始化
            this.bookItem = bookList[0]
            this.init()
          }
        }
      })
    },
    // 0、电子书初始化，以及渲染
    init () {
      const fileName = this.$route.query.fileName
      if (!this.bookItem) {
        // 找到书架中的书本
        this.bookItem = findBook(fileName)
      }
      // 0.1如果找到了，看是否有离线缓存
      if (this.bookItem) {
        // 0.1.1获取离线缓存
        getLocalForage(fileName, (err, blob) => {
          if (err || !blob) {
            // 0.1.2是否下载，此时是线上
            // this.downloadBook(fileName)
            this.isOnline = true
            const opf = this.$route.query.opf
            if (opf) {
              this.parseBook(opf)
            }
          } else {
            // 0.1.3离线模式
            this.isOnline = false
            this.parseBook(blob)
          }
        })
      } else {
        // 0.2 全部书本中找该图书
        this.findBookFrommList(fileName)
      }
    },
    // 0.1下载电子书
    downloadBook (fileName) {
      download(
        this.bookItem,
        () => {
          // 下载后回调
          getLocalForage(fileName, (err, blob) => {
            if (err) {
              return
            }
            this.parseBook(blob)
          })
        }
      )
    },
    // 0.3渲染电子书
    parseBook (blob) {
      this.book = new Epub(blob)
      // 书本详情信息
      this.book.loaded.metadata.then(metadata => {
        this.metadata = metadata
      })
      // 0.3.1-2如果是线上，url就是线上的URL
      if (this.isOnline) {
        this.book.coverUrl().then(url => {
          this.cover = url
        })
      } else {
        // 0.3.1-2否则使用加载完毕的后的封面URL
        this.book.loaded.cover.then(cover => {
          // 创建封面链接
          this.book.archive.createUrl(cover).then(url => {
            this.cover = url
          })
        })
      }
      // 0.3.2 目录信息
      this.book.loaded.navigation.then(nav => {
        this.navigation = nav
      })
      // 0.4渲染
      this.display()
    },
    // 0.4渲染
    display () {
      // 高度减去各种顶部要素
      const height = window.innerHeight * 0.9 - realPx(40) - realPx(54) - realPx(46) - realPx(48) - realPx(60) - realPx(44)
      this.rendition = this.book.renderTo('read', {
        width: window.innerWidth,
        height: height,
        method: 'default'
      })
      this.rendition.display()
    },
    // ******************************** //
    // 返回按钮
    back () {
      this.$router.back()
    },
    // 判断偏移量来进行CSS操作
    onScroll (offsetY) {
      if (offsetY > realPx(42)) {
        this.$refs.title.showShadow()
      } else {
        this.$refs.title.hideShadow()
      }
    },
    toggleContent () {
      this.ifShowContent = !this.ifShowContent
    },
    // 将一级目录转变为多级目录
    doFlatNavigation (content, deep = 1) {
      const arr = []
      content.forEach(item => {
        item.deep = deep
        arr.push(item)
        if (item.subitems && item.subitems.length > 0) {
          arr.push(this.doFlatNavigation(item.subitems, deep + 1))
        }
      })
      return arr
    },
    // 显示提示框
    showToast (text) {
      this.simpleToast(text)
    },
    // 显示音频窗体
    onPlayingCardClick () {
      this.$refs.speakWindow.show()
    },
    // 更新文本
    // 1.3 更新文本
    updateText (text) {
      this.paragraph = text
    }
  },
  mounted () {
    this.init()
  }
}
</script>

<style lang="stylus" scoped>
  @import '../../assets/styles/global.styl'
  .book-speaking
    font-size: px2rem(16)
    width: 100%
    background: white
    .content-wrapper
      width: 100%
      .book-speak-title-wrapper
        display: flex
        padding: px2rem(15)
        box-sizing: border-box
        border-bottom: px2rem(1) solid #eee
        .icon-speak-wrapper
          flex: 0 0 px2rem(40)
          leftStyle()
          .icon-speak
            font-size: px2rem(24)
            color: #999
        .speak-title-wrapper
          flex: 1
          leftStyle()
          .speak-title
            font-size: px2rem(16)
            font-weight: bold
            color: #666
        .icon-down-wrapper
          flex: 0 0 px2rem(40)
          rightStyle()
          .icon-up
            font-size: px2rem(12)
            color: #999
          .icon-down2
            font-size: px2rem(12)
            color: #999
      .book-detail-content-wrapper
        width: 100%
        border-bottom: px2rem(1) solid #eee
        box-sizing: border-box
        .book-detail-content-list-wrapper
          padding: px2rem(10) px2rem(15)
          .loading-text-wrapper
            width: 100%
            .loading-text
              font-size: px2rem(14)
              color: #999
          .book-detail-content-item-wrapper
            .book-detail-content-item
              display: flex
              padding: px2rem(15) 0
              font-size: px2rem(14)
              line-height: px2rem(16)
              color: #333
              border-bottom: px2rem(1) solid #eee
              &:last-child
                border-bottom: none
              .book-detail-content-navigation-text
                flex: 1
                width: 100%
                ellipsis()
                &.is-playing
                  color: $color-blue
                  font-weight: bold
                  margin-left: px2rem(10)
    .book-wrapper
      position: absolute
      bottom: -100%
      z-index: 100
</style>
